<template>
  <div class="cjui-area-draw-wrap">
    <div ref="canvasRef" class="cjui-area-draw">
      <!-- 图片载入元素 -->
      <img ref="imgRef" :src="imageUrl" alt="" srcset="" @load="initImgCanvas" style="display: none;">

      <!-- 区域绘制 -->
      <canvas ref="imgCanvasRef" class="cjui-area-draw-canvas"></canvas>
      <canvas ref="saveCanvasRef" class="cjui-area-draw-canvas"></canvas>
      <canvas ref="drawCanvasRef" class="cjui-area-draw-canvas"></canvas>
    </div>

    <div class="cjui-area-draw-controls">
      <el-radio-group v-model="drawType" @change="drawTypeChange">
        <el-radio-button label="水平线" value="line" />
        <el-radio-button label="矩形选区" value="rect-range" />
        <el-radio-button label="自定义选区" value="custom-range" />
      </el-radio-group>
      <el-button type="warning" @click="cancelDrawring">取消当前绘制</el-button>
      <el-button type="danger" @click="clearDrawedRange">清除绘制区域</el-button>
      <el-button type="primary" @click="saveDrawedRange">保存</el-button>
    </div>
  </div>
</template>

<script lang="ts" setup>
const message = useMessage()

const props = defineProps({
  imageUrl: {
    type: String,
    required: true,
    default: ''
  },
  defaultRangeData: {
    type: Array,
    default: () => []
  },
  config: {
    type: Object,
    default: () => ({
      // lineLimit: false, // 线性区域绘制数量限制
      // rectRangeLimit: false, // 矩形区域绘制数量限制
      // customLimit: false, // 自定义区域绘制数量限制
    })
  }
})

const canvasRef = ref() // 外部容器区域
const imgRef = ref() // 图片载入元素
const imgCanvasRef = ref() // 原始图片绘制
const drawCanvasRef = ref() // 临时绘制区域
const saveCanvasRef = ref() // 最终绘制区域

const drawType = ref<'line' | 'rect-range' | 'custom-range'>('line') // 绘制类型

const state = {
  wrapWidth: 0, // 容器宽度
  wrapHeight: 0, // 容器高度
  imgNaturalWidth: 0, // 图片真实宽度
  imgNaturalHeight: 0, // 图片真实高度
  canvasWidth: 0, // 容器内画布宽度
  canvasHeight: 0, // 容器内画布高度
  isDrawing: false, // 绘制状态
  drawedPoints: [], // 留存绘制坐标点集
  drawingPoints: [], // 当前绘制坐标点
}
const ctxState = {
  img: null,
  draw: null,
  save: null
}
const initCanvas = () => {
  state.wrapWidth = canvasRef.value.clientWidth;
  state.wrapHeight = canvasRef.value.clientHeight;

  ctxState.img = imgCanvasRef.value.getContext('2d')
  ctxState.draw = drawCanvasRef.value.getContext('2d')
  ctxState.save = saveCanvasRef.value.getContext('2d')

  // 添加绘制事件
  drawCanvasRef.value.addEventListener('click', drawImgClick)
  drawCanvasRef.value.addEventListener('mousemove', drawImgMouseMove)
  drawCanvasRef.value.addEventListener('dblclick', drawImgDblClick)
}
const destoryCanvas = () => {
  // 移除绘制事件
  drawCanvasRef.value.removeEventListener('click', drawImgClick)
  drawCanvasRef.value.removeEventListener('mousemove', drawImgMouseMove)
  drawCanvasRef.value.removeEventListener('dblclick', drawImgDblClick)
}

// 图片加载后初始画布
const initImgCanvas = () => {
  // 计算画布最佳宽高
  state.imgNaturalWidth = imgRef.value.width
  state.imgNaturalHeight = imgRef.value.height
  if (state.imgNaturalWidth / state.imgNaturalHeight < state.wrapWidth / state.wrapHeight) {
    state.canvasHeight = state.wrapHeight
    state.canvasWidth = state.wrapHeight * state.imgNaturalWidth / state.imgNaturalHeight
  } else {
    state.canvasWidth = state.wrapWidth
    state.canvasHeight = state.wrapWidth * state.imgNaturalHeight / state.imgNaturalWidth
  }

  // 初始化画布
  imgCanvasRef.value.width = state.canvasWidth
  imgCanvasRef.value.height = state.canvasHeight
  drawCanvasRef.value.width = state.canvasWidth
  drawCanvasRef.value.height = state.canvasHeight
  saveCanvasRef.value.width = state.canvasWidth
  saveCanvasRef.value.height = state.canvasHeight

  // 图片绘制
  ctxState.img.drawImage(imgRef.value, 0, 0, state.canvasWidth, state.canvasHeight)

  // 数据回显
  readRangeData(props.defaultRangeData)
}

// 数据读取
const readRangeData = (data = []) => {
  setSaveAreaData(data)
}

// 绘制-单击事件
const drawImgClick = (e: MouseEvent) => {
  state.isDrawing = true
  if (e.offsetX || e.layerX) {
    let pointX = e.offsetX === undefined ? e.layerX : e.offsetX
    let pointY = e.offsetX === undefined ? e.layerY : e.offsetY
    let lastPoint = state.drawingPoints[state.drawingPoints.length - 1] || []
    if (lastPoint[0] !== pointX || lastPoint[1] !== pointY) {
      if (drawType.value === 'line') {
        if (state.drawingPoints.length === 0) {
          state.drawingPoints.push([pointX, pointY])
        }
        if (props.config.lineLimit) {
          state.drawedPoints = state.drawedPoints.filter(el => el.type !== 'line')
          setSaveAreaData(state.drawedPoints);
        }

      } else if (drawType.value === 'rect-range') {
        if (state.drawingPoints.length === 0) {
          state.drawingPoints.push([pointX, pointY])
        }
        if (props.config.rectRangeLimit) {
          state.drawedPoints = state.drawedPoints.filter(el => el.type !== 'rect-range')
          setSaveAreaData(state.drawedPoints);
        }

      } else {
        state.drawingPoints.push([pointX, pointY])
        if (props.config.rectRangeLimit) {
          state.drawedPoints = state.drawedPoints.filter(el => el.type !== 'custom-range')
          setSaveAreaData(state.drawedPoints);
        }
      }
    }
  }
}

// 绘制-鼠标移动事件
const drawImgMouseMove = (e: MouseEvent) => {
  if (!state.isDrawing) {
    return
  }

  if (e.offsetX || e.layerX) {
    let pointX = e.offsetX === undefined ? e.layerX : e.offsetX
    let pointY = e.offsetX === undefined ? e.layerY : e.offsetY

    // 清除原绘制
    ctxState.draw.clearRect(0, 0, state.canvasWidth, state.canvasHeight)
    // 绘制点
    ctxState.draw.fillStyle = 'blue'
    state.drawingPoints.forEach((item, i) => {
      ctxState.draw.beginPath()
      ctxState.draw.arc(...item, 6, 0, 180)
      ctxState.draw.fill()
    })
    
    // 绘制动态区域
    ctxState.draw.save()
    ctxState.draw.beginPath()

    if (drawType.value === 'line') {
      state.drawingPoints.forEach((item, i) => {
        ctxState.draw.lineTo(...item)
      })
      ctxState.draw.lineTo(pointX, state.drawingPoints[0][1])

    } else if (drawType.value === 'rect-range') {
      const newDrawingPoints = [
        state.drawingPoints[0],
        [pointX, state.drawingPoints[0][1]],
        [pointX, pointY],
        [state.drawingPoints[0][0], pointY],
        state.drawingPoints[0],
      ]
      newDrawingPoints.forEach((item, i) => {
        ctxState.draw.lineTo(...item)
      })

    } else {
      state.drawingPoints.forEach((item, i) => {
        ctxState.draw.lineTo(...item)
      })
      ctxState.draw.lineTo(pointX, pointY)
    }

    ctxState.draw.lineWidth = '3'
    ctxState.draw.strokeStyle = 'blue'
    ctxState.draw.fillStyle = 'rgba(255, 0, 0, 0.3)'
    ctxState.draw.stroke()
    ctxState.draw.fill()
    ctxState.draw.restore()
  }
}

// 绘制-双击事件
const drawImgDblClick = (e: MouseEvent) => {
  if (e.offsetX || e.layerX) {
    let pointX = e.offsetX === undefined ? e.layerX : e.offsetX
    let pointY = e.offsetX === undefined ? e.layerY : e.offsetY
    let lastPoint = state.drawingPoints[state.drawingPoints.length - 1] || []
    if (lastPoint[0] !== pointX || lastPoint[1] !== pointY) {
      if (drawType.value === 'line') {
        state.drawingPoints.push([pointX, state.drawingPoints[0][1]])

      } else if (drawType.value === 'rect-range') {
        state.drawingPoints.push(
          [pointX, state.drawingPoints[0][1]],
          [pointX, pointY],
          [state.drawingPoints[0][0], pointY]
        )

      } else {
        state.drawingPoints.push([pointX, pointY])
      }
    }
  }

  // 清除绘制图册
  ctxState.draw.clearRect(0, 0, state.canvasWidth, state.canvasHeight)
  // 留存绘制图册
  state.drawedPoints.push({ type: drawType.value, value: state.drawingPoints })
  setSaveAreaData(state.drawedPoints)
  state.drawingPoints = []
  state.isDrawing = false
}

// 绘制-最终区域绘制与数据留存
const setSaveAreaData = (data = []) => {
  ctxState.save.clearRect(0, 0, state.canvasWidth, state.canvasHeight)
  data.forEach(el => {
    if (el.value.length === 0) {
      return
    }
    ctxState.save.save()
    ctxState.save.beginPath()
    el.value.forEach((item, i) => {
      ctxState.save.lineTo(...item)
    })
    ctxState.save.closePath()
    ctxState.save.lineWidth = '2'
    ctxState.save.strokeStyle = 'red'
    ctxState.save.fillStyle = 'rgba(255, 0, 255, 0.3)'
    ctxState.save.stroke()
    ctxState.save.fill()
    ctxState.save.restore()
  })
  state.drawedPoints = data
}

// 绘制类型改变
const drawTypeChange = () => {
  ctxState.draw.clearRect(0, 0, state.canvasWidth, state.canvasHeight)
  state.drawingPoints = []
  state.isDrawing = false
}

// 取消当前绘制
const cancelDrawring = () => {
  ctxState.draw.clearRect(0, 0, state.canvasWidth, state.canvasHeight)
  state.drawingPoints = []
  state.isDrawing = false
}

// 清除所有选区
const clearDrawedRange = async () => {
  await message.confirmError('确定清除所有选区？')
  ctxState.draw.clearRect(0, 0, state.canvasWidth, state.canvasHeight)
  state.drawingPoints = []
  state.isDrawing = false
  ctxState.save.clearRect(0, 0, state.canvasWidth, state.canvasHeight)
  state.drawedPoints = []
}

// 保存所有选区
const saveDrawedRange = async () => {
  console.log(state)
}

onMounted(() => {
  initCanvas()
})
onBeforeUnmount(() => {
  destoryCanvas()
})

defineExpose({
  clearDrawedRange,
  saveDrawedRange,
})
</script>

<style lang="scss">
.cjui-area-draw-wrap {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: auto;
}

.cjui-area-draw {
  width: 100%;
  flex: 1;
  position: relative;
  overflow: hidden;
}

.cjui-area-draw-canvas {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.cjui-area-draw-controls {
  padding: 10px;
  display: flex;
  justify-content: center;
  align-items: center;

  .el-radio-group {
    margin-right: 10px;
  }
}
</style>
